<template>
    <div :id="`space_${mode}`" class="space" v-if="business">
        <div :id="`container_${mode}`" class="container" :style="containerStyle">
            <!-- 顶部 -->
            <header-item
                    :title="business.name"
                    :step="activeStep"
                    :mode="mode"
                    @start="pages = 1"
                    @end="endCourse"
                    @change="changeBusiness"
            />
            <!-- 场景固定物品 -->
            <fixed-item
                    v-for="item in items"
                    :key="`fixed-${item.name}`"
                    v-if="item.type === 'fixedItem' && item.display !== false"
                    :item="item"
                    :mode="mode"
                    :parentW="counter.w"
                    :parentH="counter.h"
                    @itemDrop="itemDrop"
                    @itemClick="itemClick"
                    @itemChange="itemChange"
            />
            <!-- 浮动物品 -->
            <div class="float" :style="floatStyle">
                <float-item
                        v-for="item in items"
                        :key="`float-${item.name}`"
                        v-if="item.type === 'floatItem' && item.display !== false"
                        :item="item"
                        @itemDrop="itemDrop"
                        @itemClick="itemClick"
                />
            </div>
            <!-- 步骤提示 -->
            <div class="tooltip" v-if="tooltip" :style="tooltipStyle">
                <steps-item
                        :steps="business.steps"
                        :autoScroll="center"
                        :mode="mode"
                        :pages="pages"
                        @change="pagesGoto"
                        @duration="durations.push($event)"
                        @end="endCourse"
                />
            </div>
            <!-- 桌面凭证 -->
            <desktop-item v-if="counter"
                          :desktopArea="counter.desktopArea"
                          :items="items"
                          :dropItem="dropItem"
                          :size="counter.desktopItemSize"
                          @itemClick="itemClick"
                          @itemConfirm="itemConfirm"
                          @itemDrag="itemDrag"
            />
            <!-- 列表 -->
            <list-item
                    v-for="item in items"
                    :key="`list-${item.name}`"
                    v-if="item.type === 'list'"
                    :item="item"
                    @itemClose="itemClose"
                    @listItemClick="listItemClick"
            />
            <!-- 窗口 -->
            <win-item
                    v-for="item in items"
                    :key="`win-${item.name}`"
                    v-if="item.type === 'window'"
                    :item="item"
                    @itemClick="itemClick"
                    @itemClose="itemClose"
            />
            <!-- 应用系统 -->
            <app-item
                    v-for="item in items"
                    :key="`app-${item.name}`"
                    v-if="item.type === 'app' && item.display !== false"
                    :item="item"
                    :counter="counter"
                    :apps="apps"
                    :mode="mode"
                    @itemChange="itemChange"
                    @change="pagesChange"
                    @addResults="addResults"
            />
            <!-- 用户输入 -->
            <input-item
                    v-for="item in items"
                    :key="`input-${item.name}`"
                    v-if="item.type === 'input'"
                    :item="item"
                    @itemClose="itemClose"
                    @itemInput="itemInput"
            />
            <!-- 视频 -->
            <video
                    v-for="item in items"
                    :key="`video-${item.name}`"
                    v-if="item.type === 'video' && item.display !== false"
                    :style="videoStyle"
                    :item="item"
                    :src="item.content"
                    autoplay
                    @ended="playEnd(item)"
            />
            <!-- 柜台区域 -->
            <area-zone v-if="mode === 'edit'" :parentW="counter.w" :parentH="counter.h" />
            <!-- 考试结果 -->
            <exam-result
                    :results="examResults"
                    :ableShowResults="true"
                    :mode="mode"
                    :action="action"
                    @close="examResults = null"
                    @next="changeBusiness"
                    @leave="leaveCourse"
            />
        </div>
    </div>
</template>

<script>
    import Vue from 'vue'
    import { Drag, Drop } from 'vue-drag-drop'
    import { Base64 } from 'js-base64'
    import cloneDeep from 'lodash/cloneDeep'
    import merge from 'lodash/merge'
    import round from 'lodash/round'
    import moment from 'moment'

    import fixedItem from './components/fixedItem'
    import listItem from './components/listItem'
    import winItem from './components/windowItem'
    import inputItem from './components/inputItem'
    import desktopItem from './components/desktopItem'
    import appItem from './components/appItem'
    import stepsItem from './components/stepsItem'
    import headerItem from './components/headerItem'
    import floatItem from './components/floatItem'
    import areaZone from './components/areaZone'
    import examResult from './components/examResult'

    import { eventMap } from './data/consts'

    Vue.component('drag', Drag)
    Vue.component('drop', Drop)

    export default {
        name: 'course',
        props: ['apps', 'business', 'businessTemplate', 'mode', 'action', 'counter', 'center'],
        components: {
            'fixed-item': fixedItem,
            'list-item': listItem,
            'win-item': winItem,
            'input-item': inputItem,
            'desktop-item': desktopItem,
            'app-item': appItem,
            'steps-item': stepsItem,
            'header-item': headerItem,
            'float-item': floatItem,
            'area-zone': areaZone,
            'exam-result': examResult
        },
        data() {
            return {
                activedItem: null,
                scale: 1,
                tooltip: true,
                pages: 1,
                pagesRecords: [],
                dropItem: null,
                items: [],
                startTime: null,
                durations: [],
                results: [],
                skips: [],
                examResults: null
            }
        },
        created() {
            this.getCurrentItems()
            this.startTime = moment()
        },
        mounted() {
            this.resize()
            window.addEventListener('resize', () => {
                if (this.resizeTimeout) {
                    clearTimeout(this.resizeTimeout)
                    this.resizeTimeout = null
                }
                this.resizeTimeout = setTimeout(this.resize, 200)
            })
        },
        watch: {
            pages(newVal, oldVal) {
                this.getCurrentItems()
            },
            counter(newVal, oldVal) {
                if (this.mode === 'edit') {
                    this.items = this.counter.items
                }
            },
            deep: true
        },
        computed: {
            containerStyle() {
                if (this.counter) {
                    return {
                        width: `${this.counter.w}px`,
                        height: `${this.counter.h}px`,
                        backgroundImage: this.counter.background ? `url(/api${this.counter.background})` : ''
                    }
                }
            },
            tooltipStyle() {
                if (this.counter && this.counter.tooltipArea) {
                    return {
                        width: `${this.counter.tooltipArea.w}px`,
                        height: `${this.counter.tooltipArea.h}px`,
                        left: `${this.counter.tooltipArea.x}px`,
                        top: `${this.counter.tooltipArea.y}px`
                    }
                }
            },
            floatStyle() {
                if (this.counter && this.counter.floatArea) {
                    return {
                        width: `${this.counter.floatArea.w}px`,
                        height: `${this.counter.floatArea.h}px`,
                        left: `${this.counter.floatArea.x}px`,
                        top: `${this.counter.floatArea.y}px`
                    }
                }
            },
            videoStyle() {
                if (this.counter && this.counter.videoArea) {
                    return {
                        position: 'absolute',
                        width: `${this.counter.videoArea.w}px`,
                        height: `${this.counter.videoArea.h}px`,
                        left: `${this.counter.videoArea.x}px`,
                        top: `${this.counter.videoArea.y}px`,
                        'z-index': 4000
                    }
                }
            },
            activeStep() {
                return this.business.steps[this.getCurrentStepIndex()]
            }
        },
        methods: {
            resize() {
                const containerEl = document.getElementById(`container_${this.mode}`)
                const spaceEl = document.getElementById(`space_${this.mode}`)
                if (!spaceEl || !containerEl) {
                    return
                }
                this.scale = Math.min(
                    spaceEl.clientWidth / containerEl.clientWidth,
                    spaceEl.clientHeight / containerEl.clientHeight
                )
                if (this.center) {
                    const x = (spaceEl.clientWidth / this.scale - containerEl.clientWidth) / 2
                    const y = (spaceEl.clientHeight / this.scale - containerEl.clientHeight) / 2
                    containerEl.style.transform = `scale(${this.scale}) translate(${x}px, ${y}px)`
                } else {
                    containerEl.style.transform = `scale(${this.scale})`
                }
            },
            computeGrade(examResults, mode) {
                console.log('computeGrade >>', examResults, this.business)
                const { duration, skips, score } = examResults
                const { excellent, good, normal, totalScore } = this.business
                if (skips && skips.length > 0) {
                    return '操作未完成'
                }
                if (score >= 0 && totalScore >= 0 && score < totalScore) {
                    return '操作错误'
                }
                if (duration >= 0 && excellent > 0 && duration <= excellent) {
                    return '优秀'
                }
                if (duration >= 0 && good > 0 && duration <= good) {
                    return '良好'
                }
                if (duration >= 0 && normal > 0 && duration <= normal) {
                    return '一般'
                }
                return '操作超时'
            },
            changeBusiness(id) {
                this.$emit('change', id)
            },
            endCourse(value) {
                console.log('endCourse >>', value, this.business, this.businessTemplate)
                if(value === 'invalid_exam') {
                    this.examResults = value
                    return
                }
                const { totalScore } = this.business ? this.business : {}
                const { businessScore } = this.businessTemplate ? this.businessTemplate : {}
                const scoreRate = totalScore > 0 && businessScore > 0 ? Number(businessScore) / Number(totalScore) : 1
                const format = 'YYYY-MM-DD HH:mm:ss.SSS'
                const examResults = {
                    startTime: moment(this.startTime).format(format),
                    endTime: moment().format(format),
                    score: 0,
                    duration: 0,
                    skips: this.skips,
                    steps: this.business.steps.map((step, i) => {
                        const stepResult = {
                            title: step.title,
                            score: 0,
                            duration: 0,
                            details: []
                        }
                        const nextStep = this.business.steps[i + 1] || this.business.steps[i]
                        let itemsOfStep = validItemsByScope(this.business.scripts, step.pages, nextStep.pages)

                        itemsOfStep = itemsOfStep.filter(item => validForReport(item))

                        itemsOfStep.forEach(item => {
                            if (item.type == 'app') {
                                const app = this.business.apps.find(app => (app.name == item.name || app.name == item.content))
                                if (!app || !app.items) return
                                const appItems = app.items.filter(item => validForReport(item))
                                appItems && appItems.forEach(item => {
                                    const triedResult = this.results.filter(result => result.name == `${app.name}-${item.name}`)
                                    if (triedResult && triedResult.length > 0) {
                                        for (const result of triedResult) {
                                            stepResult.details.push(result)
                                            stepResult.score += result.score
                                        }
                                    } else {
                                        stepResult.details.push({
                                            name: `${app.name}-${item.name}`,
                                            event: eventMap[getEvent(item)],
                                            expect: getExpect(item),
                                            result: '',
                                            score: 0,
                                            stepIndex: i
                                        })
                                    }
                                })
                            } else {
                                const triedResult = this.results.filter(result => result.uid === item.uid)
                                if (triedResult && triedResult.length > 0) {
                                    for (const result of triedResult) {
                                        stepResult.details.push(result)
                                        stepResult.score += result.score
                                    }
                                } else {
                                    stepResult.details.push({
                                        uid: item.uid,
                                        name: item.name,
                                        event: eventMap[getEvent(item)],
                                        expect: getExpect(item),
                                        result: '',
                                        score: 0,
                                        stepIndex: i
                                    })
                                }
                            }
                        })

                        this.durations.forEach(obj => {
                            if (obj.stepIndex === i) {
                                stepResult.duration += obj.duration
                            }
                        })
                        return stepResult
                    })
                }
                examResults.steps.forEach(step => {
                    examResults.score += step.score
                    examResults.duration += step.duration
                })
                this.examResults = examResults
                const { dispatch } = this.$store
                const { course } = this.$route.query
                const query = JSON.parse(Base64.decode(course))
                const { businessId, trainingId, learningId, examId, examPaperId } = query

                if (this.mode === 'study' && (this.action === 'preview' || this.action === 'edit_preview')) {
                    return
                }
                if (this.mode === 'exam' && (this.action === 'preview' || this.action === 'edit_preview')) {
                    return
                }
                if (this.mode === 'study' && trainingId && learningId) {
                    dispatch('course/submitLearningStepResult', {
                        trainingTaskId: trainingId,
                        learningTaskId: learningId,
                        businessTemplateId: businessId,
                        opStartTime: examResults.startTime,
                        opEndTime: examResults.endTime,
                        score: examResults.score,
                        grade: this.computeGrade(examResults, 'study'),
                        learnStepsContent: JSON.stringify(examResults)
                    })
                } else if (this.mode === 'exam' && examId) {
                    // 设置考试为完成参加
                    dispatch('course/completeExam', examId)

                    // 保存考试成绩
                    const finalResult = {
                        examinationId: examId,
                        examinationPaperId: examPaperId,
                        businessTemplateId: businessId,
                        opStartTime: examResults.startTime,
                        opEndTime: examResults.endTime,
                        score: round(examResults.score * scoreRate, 1),
                        grade: this.computeGrade(examResults, 'exam'),
                        examStepsContent: JSON.stringify(examResults)
                    }
                    console.log("exam final result >> ", finalResult)
                    dispatch('course/submitExamStepResult', finalResult)
                }
            },
            leaveCourse() {
                const { dispatch } = this.$store
                if (this.mode === 'study' && this.action === 'preview') {
                    this.$router.push({ name: 'trainTask' })
                    return
                }
                if (this.mode === 'study' && this.action === 'edit_preview') {
                    return
                }
                if (this.mode === 'exam' && this.action === 'preview') {
                    this.$router.push({ name: 'examManagement' })
                    return
                }

                if (this.mode === 'study') {
                    this.$router.push({
                        name: 'traineeHome',
                        query: { tabName: 'learning' }
                    })
                    return
                }
                if (this.mode === 'exam') {
                    this.$router.push({ name: 'traineeHome', query: { tabName: 'exam' } })
                    return
                }
            },
            getCurrentItems() {
                if (!this.counter || !this.business) {
                    return
                }
                if (this.mode === 'edit') {
                    this.items = this.counter.items
                } else {
                    this.items = getItemsBypages(cloneDeep([...this.counter.items, ...this.business.scripts]), this.pages)
                }
            },
            valueEqual(expect = '', result = '') {
                if (typeof expect === 'string' && expect.indexOf('|') > -1) {
                    // 重排
                    const a = expect
                        .split('|')
                        .sort()
                        .join('|')
                    const b = result
                        .split('|')
                        .sort()
                        .join('|')
                    return a == b
                } else {
                    return expect == result
                }
            },
            execute(item, event, expect, result) {
                const { show, hide, pages, lastValue, score } = item[event]
                if ((this.mode === 'study' && this.valueEqual(expect, result)) || this.mode === 'exam') {
                    this.itemShow(show, lastValue)
                    this.itemHide(hide)
                    this.pagesChange(item, event, pages)
                }
                if (score) {
                    /** save results */
                    this.results.push({
                        uid: item.uid,
                        name: item.name,
                        event: eventMap[event],
                        expect,
                        result,
                        score: this.valueEqual(expect, result) ? score : 0,
                        stepIndex: this.getCurrentStepIndex()
                    })
                }
                console.log('execute >> ', item, event, this.pages)
            },
            addResults(result) {
                result.stepIndex = this.getCurrentStepIndex()
                this.results.push(result)
            },
            getCurrentStepIndex() {
                let stepIndex = 0
                this.business.steps.forEach((step, i) => {
                    if (this.pages >= step.pages) {
                        stepIndex = i
                    }
                })
                return stepIndex
            },
            itemShow(show, lastValue) {
                if (show) {
                    const shows = show.split(',')
                    this.items.forEach(item => {
                        shows.forEach(v => {
                            if (item.name === v) {
                                item.display = true
                                item.lastValue = lastValue
                            }
                        })
                    })
                }
            },
            itemHide(hide) {
                if (hide) {
                    const hides = hide.split(',')
                    this.items.forEach(item => {
                        hides.forEach(v => {
                            if (item.name === v) {
                                item.display = false
                            }
                        })
                    })
                }
            },
            itemChange(item) {
                let origin = this.items.find(o => o.name === item.name)
                origin = item
            },
            pagesChange(item, event, pages) {
                const exist = this.pagesRecords.some(r => r.uid === item.uid && r.event === event && r.pages === pages)
                if (pages !== undefined && !exist) {
                    this.pages += pages
                    this.pagesRecords.push({ uid: item.uid, event, pages, page: this.pages })
                    // item[event].pages = 0
                }
            },
            pagesGoto(pages) {
                if (this.mode === 'exam') {
                    let step
                    for (let i in this.business.steps) {
                        if (this.pages >= this.business.steps[i].pages) {
                            step = this.business.steps[i]
                        }
                    }
                    if (pages) {
                        const existStep = this.skips.find(item => item.pages === step.pages)
                        if (!existStep) {
                            this.skips.push({
                                pages: step.pages,
                                step: step.title,
                                from: this.pages,
                                to: pages
                            })
                        } else {
                            console.log("Duplicated skip, ignored here!")
                        }
                    }
                    if (!pages) {
                        this.endCourse()
                    }
                }
                if (pages !== undefined) {
                    this.pages = pages
                    this.pagesRecords = this.pagesRecords.filter(r => r.page < pages)
                }
            },
            itemDrag(item) {
                if (item.onDrag) {
                    const { value, pages } = item.onDrag
                    if (value && this.dropItem) {
                        this.execute(item, 'onDrag', value, this.dropItem.name)
                        if (this.dropItem.value) {
                            this.dropItem.value += `|${item.name}`
                        } else {
                            this.dropItem.value = item.name
                        }
                        if (this.dropItem.onChange) {
                            this.execute(this.dropItem, 'onChange', this.dropItem.onChange.value, this.dropItem.value)
                        }
                    }
                }
            },
            itemDrop(item) {
                this.dropItem = item
            },
            itemClick(item) {
                if (item.onClick) {
                    this.execute(item, 'onClick')
                }
                if (this.mode === 'edit') {
                    this.$store.commit('course/update', { activedItem: item })
                    this.$store.commit('course/update', { activedConfig: 'item' })
                }
            },
            listItemClick(item, v) {
                if (item.onChange) {
                    const { value } = item.onChange
                    item.onChange.lastValue = v
                    this.execute(item, 'onChange', value, v)
                }
                if (item.onClick) {
                    const { value } = item.onClick
                    item.onClick.lastValue = v
                    this.execute(item, 'onClick')
                }
                item.display = false
            },
            itemInput(item) {
                if (item.onChange) {
                    const { value, pages, show } = item.onChange
                    const finalValue = item.lastValue ? `${item.lastValue},${item.content}` : item.content
                    this.execute(item, 'onChange', value, finalValue)
                }
                item.display = false
            },
            itemConfirm(item, result) {
                if (item.onChange && item.onChange.value) {
                    this.execute(item, 'onChange', item.onChange.value, result)
                }
            },
            itemClose(item) {
                item.display = false
            },
            playEnd(item) {
                if (item.onChange) {
                    this.execute(item, 'onChange')
                }
                this.itemClose(item)
            }
        }
    }

    function getExpect(item) {
        if (item.onChange && item.onChange.value) {
            return item.onChange.value
        }
        if (item.onClick && item.onClick.value) {
            return item.onClick.value
        }
        if (item.onDrag && item.onDrag.value) {
            return item.onDrag.value
        }
    }

    function getEvent(item) {
        if (item.onChange && item.onChange.pages > 0) {
            return 'onChange'
        }
        if (item.onClick && item.onClick.pages > 0) {
            return 'onClick'
        }
        if (item.onDrag && item.onDrag.pages > 0) {
            return 'onDrag'
        }
    }

    function validForReport(item) {
        return (item.onChange && item.onChange.score > 0)
            || (item.onClick && item.onClick.score > 0)
            || (item.onDrag && item.onDrag.score > 0)
            || item.type == 'app'
    }

    function isValidItemByScope(scopes, min, max) {
        min = min === null ? 0 : min
        const groups = scopes.split(',')
        let valid = false
        groups.forEach(group => {
            if (!Number.isNaN(group) && group >= min && (group < max || null === max)) {
                valid = true
            }
            if (group.indexOf('-') > -1) {
                const [start, end] = group.split('-').map(num => Number(num))
                if (end >= min && start < max) {
                    valid = true
                }
            }
            if (group.indexOf('+') > -1) {
                const pos = group.indexOf('+')
                const num = Number(group.replace('+', ''))
                if (pos === 0) {
                    if (num >= min) {
                        valid = true
                    }
                } else {
                    if (num < max || null === max) {
                        valid = true
                    }
                }
            }
        })
        return valid
    }

    function validItemsByScope(scripts, min, max) {
        return scripts.filter(script => isValidItemByScope(script.scope, min, max))
    }

    function getItemsBypages(items, pages) {
        let currentItems = items.filter(item => {
            const { scope } = item
            const groups = scope.split(',')
            let valid = false
            groups.forEach(group => {
                if (group == pages) {
                    valid = true
                }
                if (group.indexOf('-') > -1) {
                    const [start, end] = group.split('-').map(num => Number(num))
                    if (pages >= start && pages <= end) {
                        valid = true
                    }
                }
                if (group.indexOf('+') > -1) {
                    const pos = group.indexOf('+')
                    const num = Number(group.replace('+', ''))
                    if (pos === 0) {
                        if (pages <= num) {
                            valid = true
                        }
                    } else {
                        if (pages >= num) {
                            valid = true
                        }
                    }
                }
            })
            return valid
        })
        const mergedItems = currentItems.reduce((last, curr) => {
            let item = last.find(item => item.name === curr.name)
            if (item) {
                merge(item, curr)
            } else {
                last.push(curr)
            }
            return last
        }, [])
        return mergedItems
    }
</script>

<style lang="less" scoped>
    .space {
        width: 100%;
        height: 100%;
    }

    .container {
        position: relative;
        background-repeat: round;
        background-size: contain;
        transform-origin: top left;
    }

    .tooltip {
        position: absolute;
    }

    .float {
        position: absolute;
    }
</style>
<style lang="less">
    .el-dialog {
        border-radius: 10px;

        .el-dialog__header {
            box-shadow: 5px 5px 15px rgba(200, 200, 200, 0.2);
            clip-path: inset(0px 0px -15px 0px);

            .el-dialog__title {
                color: #ff6900;
                font-weight: 500;
            }
        }

        .el-dialog__body {
            h2 {
                font-size: 18px;
                text-align: center;
                background-color: rgba(200, 200, 200, 0.3);
                border-radius: 3px;
                padding: 10px;
                margin: 10px;
            }
        }
    }
</style>


